
/**
  * @brief   AT框架
  * @author  林勇安
  * @version V1.0
  * @date    2024年9月8日
  * @last    2024年10月5日
  * @expl  <框架数据结构>:
           1、全局操控句柄SAT_Obj,一个句柄对象就是一个独立的AT模块,对象化设计,多模块间互不干扰.
           2、数据处理缓存,SAT_Obj->r_buf,扁平化空间,模块回复的任何内容都将存放到该缓存内.
           4、指令对象句柄SAT_ExecObj,每次调用指令执行函数,都要定义该对象的局部变量,作为参数传入函数中.
           5、指令结果缓存,SAT_ExecObj->content,用来存放指令的返回内容.
           6、针对有多通道通信的模块,如4G/WIFI/蓝牙,提供通道管理API,包括通道绑定、通道写入、通道读取.
              对于4G/WIFI等网络模块,一个通道可以看作是一个fd的接收buf.
  * @expl  <事件触发机制>:
           当收到事件列表中的event_str且收到表示"结尾"的N个字符后,才会触发事件回调函数.

  * @expl  <指令执行有四种API>:
           1、只执行指令,没有期望内容,也不关心收到多少个结尾(一个结尾代表一行,下同).
           2、执行指令,没有期望内容,只关心收到多少个结尾.
           3、执行指令,只有期望内容,不关心收到多少个结尾.
           4、执行指令,既关心期望内容,也关心收到多少个结尾,即只有收到期望内容和N个结尾后,才算成功.

  * @expl  <SAT_Obj->r_buf和SAT_ExecObj->content的区别>:
           SAT_Obj->r_buf存放AT模块的返回内容,经AT_parse_task()处理,最终提取出需要的结果,放到SAT_ExecObj->content中.
           每次调用指令执行函数AT_exec_xxx(),都会加互斥锁,在执行过程中,会将结果放入SAT_ExecObj->content,
           而后解锁,这就保证了,在执行指令时,不会因抢占而导致数据冲突.
           而在调用该框架的各模块驱动应用中,不用考虑加锁的问题,因为每条指令执行都有自己的SAT_ExecObj,指令执行完后,
           执行结果一定是存放在SAT_ExecObj->content中的,不会因抢占而导致数据冲突.
           也就是说,在模块驱动应用中,可以随心所欲的在任意时刻调用多个AT_exec_xxx()指令,不用担心数据冲突问题.
  */

#include "my_at.h"

/*----------移植层----------*/
/**
  * @brief 串口初始化.
  * @return MY_OK:成功 MY_ERR:失败.
  */
static int AT_uart_init(int uart_no, uInt baud, uChar databit, uChar stopbit, uChar parity)
{
    return com_uart_init(uart_no, baud, databit, stopbit, parity, 1024);
}

/**
  * @brief 串口发送.
  * @return >=0:发送的字节数.
            MY_ERR:失败.
  */
static int AT_uart_send(int uart_no, const void *data, int size)
{
    return com_uart_send(uart_no, (void *)data, size);
}

/**
  * @brief 串口读取.
  * @return >=0:收到的字节数.
            MY_ERR:失败.
  */
static int AT_uart_read(int uart_no, void *data, int size)
{
    return com_uart_read(uart_no, data, size);
}

/*----------应用层----------*/

/**
  * @brief 清空SAT_Obj->r_buf缓存.
  * @para1 SAT_Obj.
  * @expl  
  * @return MY_OK:成功 MY_ERR:失败.
  */
static void AT_clr_buffer(SAT_Obj *handle)
{
    handle->r_index = 0;
    MY_MEMSET(handle->r_buf, 0, handle->r_size);
}

/**
  * @brief 改变表示结尾的字符串内容.
  * @para1 SAT_Obj.
  * @para2 表示结尾的字符串,必须是全局或const字符串,不能是变量.
  * @expl  公认的AT指令结尾为"\r\n",
           为了防止某个奇葩厂家乱玩,
           特此开放此API,可以自定义结尾字符串.
  * @expl  可在任意时刻任意地方执行任意次,执行即生效.
  * @expl  该框架默认的结尾为"\r\n",若无需更改,则不必调用此API.
  * @return MY_OK:成功 MY_ERR:失败.
  */
int AT_set_tail(SAT_Obj *handle, const char *tail)
{
    handle->tail = tail;
    handle->tail_size = MY_STRLEN(tail);
    return MY_OK;
}

/**
  * @brief 执行一条AT指令,不关心任何回复,不关心有多少个结尾.等待延时到达即返回.
  * @para1 全局变量,一个全局对象就是一个独立的AT模块.
  * @para2 局部变量,一个对象与一条指令相关.
  * @para3 要执行的AT指令,字符串.
  * @para4 cmd的长度,若写0,则函数内部会使用MY_STRLEN()计算长度.
  * @para5 发送完指令后的等待时间,可为0,单位ms.
  * @expl  
  * @return MY_OK:成功 MY_ERR:失败.
  */
int AT_exec_expect_none(SAT_Obj *handle, SAT_ExecObj *exec, const void *cmd, int cmd_len, int wait_time)
{   
    return AT_exec_expect_all(handle, exec, cmd, cmd_len, MY_NULL, 0, wait_time);
}

/**
  * @brief 执行一条AT指令,不关心内容,只期望收到tail_num个结尾.
  * @para1 全局变量,一个全局对象就是一个独立的AT模块.
  * @para2 局部变量,一个对象与一条指令相关.
  * @para3 要执行的AT指令,字符串.
  * @para4 cmd的长度,若写0,则函数内部会使用MY_STRLEN()计算长度.
  * @para5 期望收到的结尾个数,最小值为1.
  * @para6 发送完指令后的等待时间,可为0,单位ms.
  * @expl  收到tail_num个结尾后立即返回,否则等到wait_time到达后返回.
  * @expl  "tail_num个结尾"不是指写在一起的多个结尾,如"\r\n\r\n",而是指多条回复,
           比如,4G回复了“+CSQ:17,0\r\nOK\r\n”,这就是两个结尾,tail_num值为2.
           当然,"\r\n\r\n"也会被认为是两个结尾,而判定成功.
  * @expl  
  * @return MY_OK:成功 MY_ERR:失败.
  */
int AT_exec_expect_tail(SAT_Obj *handle, SAT_ExecObj *exec, const void *cmd, int cmd_len, int tail_num, int wait_time)
{   
    if(tail_num <= 0) tail_num = 1;
    return AT_exec_expect_all(handle, exec, cmd, cmd_len, MY_NULL, tail_num, wait_time);
}

/**
  * @brief 执行一条AT指令,不关心结尾个数,只期望收到指定内容.
  * @para1 全局变量,一个全局对象就是一个独立的AT模块.
  * @para2 局部变量,一个对象与一条指令相关.
  * @para3 要执行的AT指令,字符串.
  * @para4 cmd的长度,若写0,则函数内部会使用MY_STRLEN()计算长度.
  * @para5 期望收到的字符串内容.
  * @para6 发送完指令后的等待时间,可为0,单位ms.
  * @expl  收到包含期望内容的整行数据立即返回,否则等到wait_time到达后返回.
  * @expl  返回结果后,可使用AT_get_buffer()获取接收缓存信息,处理数据.
  * @return MY_OK:成功 MY_ERR:失败.
  */
int AT_exec_expect_data(SAT_Obj *handle, SAT_ExecObj *exec, const void *cmd, int cmd_len, const char *expect_data, int wait_time)
{   
    return AT_exec_expect_all(handle, exec, cmd, cmd_len, expect_data, 0, wait_time);
}

/**
  * @brief 执行一条AT指令,期望收到指定内容和tail_num个结尾.
  * @para1 全局变量,一个全局对象就是一个独立的AT模块.
  * @para2 局部变量,一个对象与一条指令相关.
  * @para3 要执行的AT指令/要发送的内容.
  * @para4 cmd的长度,若写0,则函数内部会使用MY_STRLEN()计算长度.
  * @para5 期望收到的字符串,可为MY_NULL.程序内部采用部分匹配的方式,只要收到的内容里有该字符串,就算匹配成功.
  * @para6 期望收到的结尾个数,0~N.
  * @para7 发送完指令后的等待时间,可为0,单位ms.
  * @expl  收到指定内容和tail_num个结尾后立即返回,否则等到wait_time到达后返回.
  * @expl  期望收到的字符串,可以在返回内容的任意一条中,不必非在第一条,
           比如期望收到"CSQ",要求的tail_num为2,则:
           “+CSQ:17,0\r\nOK\r\n”和“OK\r\n+CSQ:17,0\r\n”两种回复都是符合要求的.
  * @expl  "tail_num个结尾"不是指写在一起的多个结尾,如"\r\n\r\n",而是指多条回复,
           比如,4G回复了“+CSQ:17,0\r\nOK\r\n”,这就是两个结尾,tail_num值为2.
           当然,"\r\n\r\n"也会被认为是两个结尾,而判定成功.
  * @expl  返回结果后,可使用AT_get_buffer()获取接收缓存信息,处理数据.
  * @return MY_OK:成功 MY_ERR:失败.
  */
int AT_exec_expect_all(SAT_Obj *handle, SAT_ExecObj *exec, const void *cmd, int cmd_len, const char *expect_data, int tail_num, int wait_time)
{   
    int ret;
    int cycle;
    
    rtos_mutex_lock(handle->mutex);

    handle->exec = exec;
    handle->exec_tail_count = 0;
    handle->exec_expect = expect_data;
    handle->exec_tail_num = tail_num;
    handle->exec_finish = 0;
        
    exec->status = 0;
    exec->index = 0;
    for(int i=0; i<AT_EXEC_BUF_SIZE; i++){
        exec->content[i] = 0;
    }
    
    do{
        if(cmd_len == 0) cmd_len = MY_STRLEN(cmd);
        ret = AT_uart_send(handle->uart_no, cmd, cmd_len);
        if(ret == MY_ERR){
            exec->status = AT_EXEC_ERROR;
            break;
        }

        if((handle->exec_expect==MY_NULL)&&(handle->exec_tail_num==0)){
            rtos_sleep(wait_time);
            exec->status = AT_EXEC_OK;
            ret = MY_OK; 
            break;
        }

        cycle = 1;
        while(cycle){
            ret = rtos_sem_wait(handle->exec_sem, wait_time);
            if(ret == MY_OK){
                if((exec->index + handle->r_index) >= AT_EXEC_BUF_SIZE){
                    MY_PRINTF("exec->content 溢出了!!!!\n");
                    exec->status = AT_EXEC_ERROR;
                    break;
                }
                
                for(int i=0; i<handle->r_index; i++){
                    exec->content[exec->index++] = handle->r_buf[i];
                }
                
                AT_clr_buffer(handle);
                
                if(handle->exec_finish == 1){
                    exec->status = AT_EXEC_OK;
                    handle->exec = MY_NULL;
                    handle->exec_tail_num = 0;
                    handle->exec_tail_count = 0;
                    handle->exec_expect = MY_NULL;
                    cycle = 0;
                }
                
                rtos_sem_post(handle->sem);
            }else{
                exec->status = AT_EXEC_ERROR;
                handle->exec = MY_NULL;
                handle->exec_tail_num = 0;
                handle->exec_tail_count = 0;
                handle->exec_expect = MY_NULL;
                cycle = 0;
            }
        }
    }while(0);

    rtos_mutex_unlock(handle->mutex);

    return ret;
}

/**
  * @brief 继续读取触发事件的后续字节,直到遇到结尾.
  * @para1 SAT_Obj.
  * @expl  此函数只能在事件回调函数中调用,不能在其它地方调用.
  * @expl  调用此函数,会持续读取事件的后续字节,直到读到"结尾"才返回,返回此次调用读到的字节数.
  * @expl  当此函数返回0,并不代表模拟触发事件后传递完成所有内容,有可能是串口还未接收完毕,
           需要结合实际回调函数的功能来决定是否继续使用此函数接收触发事件的后续内容.
  * @expl  虽然此函数读到"结尾"才返回,但不代表已经读完了所有内容,比如,TCP传来数据,在数据中也
           可能存在"结尾",该"结尾"并不是AT指令的结尾,需要结合实际回调函数的功能来决定是否继续使用
           此函数接收触发事件的后续内容.
  * @return MY_OK:成功. MY_ERR:失败.
  */
int AT_event_continue_read(SAT_Obj *handle)
{   
    int i,len=0;
    char buf;

    int over_time = 2000;
    
    while(1){
        /*读取数据,一次只读一个字节*/
        len = AT_uart_read(handle->uart_no, &buf, 1);

        /*超时判定.(找不到结尾且无后续数据到来,才会触发超时判定)*/
        if(len <= 0){
            if(over_time){
                over_time--;
                if(over_time <= 0){
                    over_time = 0;
                    return MY_ERR;
                }
            }
            rtos_sleep(1);
            continue;
        }else{
            over_time = 2000;
        }
        
        /*缓存溢出判定*/
        if((handle->r_index+len) >= handle->r_size){
            MY_PRINTF("my_at.c[event]缓存溢出,缓存大小%d\n", handle->r_size);
            return MY_ERR;
        }

        /*数据转移至缓存*/
        handle->r_buf[handle->r_index++] = buf;

        /*----数据解析----*/
        
        /*整行判定*/
        if(handle->r_index < handle->tail_size){
            continue;
        }
        for(i=0; i<handle->tail_size; i++){
            if(handle->r_buf[handle->r_index-handle->tail_size+i]!=handle->tail[i]){
                break;
            }
        }
        if(i != handle->tail_size){ //与"结尾"不匹配,不是一整行
            continue;
        }
        break;
    }
    return MY_OK;
}

/**
  * @brief 获取存放事件所有内容的缓存,实际上就是SAT_Obj的r_buf指针.
  * @para1 SAT_Obj.
  * @para2 存放事件所有内容的字节数.
  * @expl  也可直接使用SAT_Handle结体体来获取r_buf,依喜好自由决定.
  * @expl  此函数只能在事件回调函数中调用,不要在其它地方调用.
  * @return 指向事件内容的指针,指向SAT_Handle中的r_buf.
  */
char *AT_event_get_buffer(SAT_Obj *handle, int *len)
{
    *len = handle->r_index;
    return handle->r_buf;
}

/**
  * @brief 通信句柄与AT通道绑定.
  * @para1 SAT_Obj.
  * @para2 通信句柄,网络fd,比如连接TCP后的fd.
  * @expl  会自动寻找未使用通道进行绑定.
  * @return MY_OK:成功 MY_ERR:失败.
  */
int AT_channel_bind(SAT_Obj *handle, int fd)
{
    int chan_no = -1;
    
    /*<遍历所有通道,防止重复绑定,并记录未使用通道*/
    for(int i=0; i<AT_CHANNEL_NUM; i++){
        //记录一个未使用通道
        if((chan_no==-1) && (handle->chan[i].used==0)){
            chan_no = i;
        }
        //重复绑定判定
        if((handle->chan[i].fd==fd) && (handle->chan[i].used==1)){
            return MY_OK;
        }
    }

    /*<通道已满*/
    if(chan_no == -1){
        return MY_ERR;
    }

    /*<初始化通道-有防重复初始化机制,不用担心分配多余内存*/
    SAT_Channel *p = &handle->chan[chan_no];
    if(p->inited == 0){
        p->size = AT_CHANNEL_BUF_SIZE;
        p->rp = p->wp = 0;
        p->data = MY_NULL;
        p->data = MY_NEW(uChar, p->size);
        if(p->data == MY_NULL){
            return MY_ERR;
        }
        p->inited = 1;
    }

    /*<绑定新通道*/
    handle->chan[chan_no].fd = fd;
    handle->chan[chan_no].used = 1;

    return MY_OK;
}

/**
  * @brief 通信句柄与AT通道解除绑定.
  * @para1 SAT_Obj.
  * @para2 通信句柄,网络fd,比如连接TCP后的fd.
  * @expl  只解除通道绑定,通道已分配内存的缓存,不做释放.
  * @return MY_OK:成功 MY_ERR:失败.
  */
int AT_channel_unbind(SAT_Obj *handle, int fd)
{
    int i;

    for(int i=0; i<AT_CHANNEL_NUM; i++){
        if((handle->chan[i].fd==fd)&&(handle->chan[i].used==1)){
            handle->chan[i].used = 0;
            handle->chan[i].fd = -1;
            return MY_OK;
        }
    }
    
    return MY_ERR;
}

/**
  * @brief 向通道写入数据.
  * @para1 SAT_Obj.
  * @para2 通信句柄,网络fd,比如连接TCP后的fd.(不是AT框架的通道号).
  * @para3 要写入的数据.
  * @para4 字节数.
  * @expl  这个函数不是用来发送数据的,是用来存放从4G/WIFI等模块接收到的数据.
           4G/WIFI收到数据据,触发事件回调,在事件回调函数中处理指令内容后提取出数据,
           将数据写入通道.而后就能用下面的AT_read_channel()读出网络fd发来的数据内容了.
  * @return MY_OK:成功 MY_ERR:失败或已满.
  */
int AT_channel_write(SAT_Obj *handle, int fd, void *data, int size)
{
    int i,chan_no = -1;
    
    for(i=0; i<AT_CHANNEL_NUM; i++){
        if((handle->chan[i].fd==fd) && (handle->chan[i].used==1)){
            chan_no = i;
            break;
        }
    }
    if(chan_no == -1){
        return MY_ERR;
    }

    uInt rp, wp;
    SAT_Channel *p = &handle->chan[chan_no];
    uChar *p_data = (uChar *)data;
    if(p->wp >= p->size){
        return MY_ERR;
    }
    rp = p->rp;
    for(i=0; i<size; i++){
        wp = (p->wp + 1) & (p->size - 1);
        if(wp == rp){
            return MY_ERR;
        }
        p->data[p->wp] = p_data[i];
        p->wp = wp;
    }
    
    return MY_OK;
}

/**
  * @brief 从通道读出数据.
  * @para1 SAT_Obj.
  * @para2 通信句柄,网络fd,比如连接TCP后的fd.(不是AT框架的通道号).
  * @para3 存数据的数组.
  * @para4 字节数.
  * @expl  
  * @return MY_ERR:失败. >=0:读到的字节数.
  */
int AT_channel_read(SAT_Obj *handle, int fd, void *data, int size)
{
    int i, chan_no = -1;
    
    for(i=0; i<AT_CHANNEL_NUM; i++){
        if((handle->chan[i].fd==fd) && (handle->chan[i].used==1)){
            chan_no = i;
            break;
        }
    }
    if(chan_no == -1){
        return MY_ERR;
    }

    uInt wp;
    SAT_Channel *p = &handle->chan[chan_no];
    uChar *p_data = (uChar *)data;
    if(p->rp >= p->size){
        return MY_ERR;
    }
    wp = p->wp;
    for(i=0; i<size; i++){
        if(p->rp == wp){
            break;
        }
        p_data[i] = p->data[p->rp];
        p->rp = (p->rp + 1) & (p->size - 1);
    }
    
    return i;
}

/**
  * @brief 从通道读出数据.
  * @para1 SAT_Obj.
  * @para2 通信句柄,网络fd,比如连接TCP后的fd.(不是AT框架的通道号).
  * @para3 存数据的数组.
  * @para4 字节数.
  * @para5 超时时间,ms.
  * @expl  超时后返回实际读到的字节数.
           若未超时前读到了size指定的字节数,则立即返回.
  * @return MY_ERR:失败. >=0:读到的字节数.
  */
int AT_channel_read_timeout(SAT_Obj *handle, int fd, void *data, int size, int timeout)
{
    int i, chan_no = -1;
    
    for(i=0; i<AT_CHANNEL_NUM; i++){
        if((handle->chan[i].fd==fd) && (handle->chan[i].used==1)){
            chan_no = i;
            break;
        }
    }
    if(chan_no == -1){
        return MY_ERR;
    }

    //uInt wp;
    SAT_Channel *p = &handle->chan[chan_no];
    uChar *p_data = (uChar *)data;
    if(p->rp >= p->size){
        return MY_ERR;
    }
    //wp = p->wp;
    for(i=0; i<size; i++){
        while(p->rp == p->wp){
            rtos_sleep(5);
            timeout -= 5;
            if(timeout <= 0){
                return i;
            }
        }
        p_data[i] = p->data[p->rp];
        p->rp = (p->rp + 1) & (p->size - 1);
    }
    
    return i;
}


/**
  * @brief AT解析任务.
  * @expl  AT框架的核心函数.
  * @expl  接收到数据后,视为非空闲状态.未接收到数据后,空闲开始计数,5秒后仍无数据,视为空闲状态,会清空缓存.
  */
static void AT_parse_task(void *arg)
{
    int i,len;
    int idle_count = 0; //空闲计时,单位ms
    char buf;
    
    SAT_Obj *handle = (SAT_Obj *)RTOS_PARA2(arg);
    
    int wait_time = 10;
    
    while(1){
        /*读取数据*/
        len = AT_uart_read(handle->uart_no, &buf, 1);

        /*空闲判定,清空缓存*/
        if(len <= 0){
            if(idle_count){ 
                idle_count -= wait_time;
                if(idle_count <= 0){
                    idle_count = 0;
                    AT_clr_buffer(handle);
                }
            }
            rtos_sleep(wait_time);
            continue;
        }else{
            idle_count = 5000;
        }
        
        /*缓存溢出判定,清空缓存*/
        if((handle->r_index+len) >= handle->r_size){
            MY_PRINTF("缓存溢出,清空全部缓存\n");
            AT_clr_buffer(handle);
            continue;
        }

        /*数据转移至缓存*/
        handle->r_buf[handle->r_index++] = buf;


        /*----数据解析----*/
        
        /*整行判定*/
        if(handle->r_index < handle->tail_size){
            continue;
        }
        for(i=0; i<handle->tail_size; i++){
            if(handle->r_buf[handle->r_index-handle->tail_size+i] != handle->tail[i]){
                break;
            }
        }
        if(i != handle->tail_size){ //与"结尾"不匹配,不是一整行
            continue;
        }

        /*结尾有效性判定-结尾以\r\n为例：
          有些模块一帧AT指令会在开头也加\r\n,影响我的解析功能,
          所有当开头是\r\n时,把这个\r\n去掉,不做判断.
          即模块若只发个结尾,或者发一帧以结尾开头的东西,则结尾会被过滤掉,相当于模块没发*/
        if(handle->r_index == handle->tail_size){
            AT_clr_buffer(handle);
            continue;
        }

        /*只有上面判断为已经收到一整行了,才会执行下面的解析内容*/

        //事件判定
        for(i=0; i<handle->event_num; i++){
            //if(MY_STRNCMP(handle->event[i].event_str, handle->r_buf, MY_STRLEN(handle->event[i].event_str)) == 0){
            if(MY_STRSTR(handle->r_buf, handle->event[i].event_str) != MY_NULL){
                handle->event[i].callback();   //注意:回调中只能获取并处理回调需要的字节,不能多读内容
                AT_clr_buffer(handle);
                break;
            }
        }
        if(i != handle->event_num){ //此行内容与事件匹配,并且执行了事件,则直接continue
            continue;
        }
        
        //期望内容判定1==>既无期望内容,又无期望结尾个数,则不对数据进行处理,清空缓存
        if((handle->exec_expect==MY_NULL) && (handle->exec_tail_num==0)){
            AT_clr_buffer(handle);
            continue;
        }

        /*通知exec指令执行函数转移数据*/
        handle->exec_finish = 0;
        rtos_sem_post(handle->exec_sem);
        rtos_sem_wait(handle->sem, 500);

        //期望内容判定2==>无期望内容,有期望结尾个数
        if((handle->exec_expect==MY_NULL) && (handle->exec_tail_num)){
            handle->exec_tail_count++;
            if(handle->exec_tail_count >= handle->exec_tail_num){
                handle->exec_finish = 1;
                rtos_sem_post(handle->exec_sem);
                rtos_sem_wait(handle->sem, 500);
            }
            continue;
        }

        //期望内容判定3==>有期望内容,无期望结尾个数
        if((handle->exec_expect!=MY_NULL) && (handle->exec_tail_num==0)){           
            if(handle->exec != MY_NULL){
                if(MY_STRSTR(handle->exec->content, handle->exec_expect)){
                    handle->exec_finish = 1;
                    rtos_sem_post(handle->exec_sem);
                    rtos_sem_wait(handle->sem, 500);
                }
            }
            continue;
        }
        
        //期望内容判定4==>有期望内容,有期望结尾个数
        if((handle->exec_expect!=MY_NULL)&&(handle->exec_tail_num)){
            handle->exec_tail_count++;
            if(handle->exec_tail_count >= handle->exec_tail_num){
                //结尾个数满足了,期望内容也满足了,指令完成
                //结尾个数满足了,期望内容却没满足,指令失败
                if(handle->exec != MY_NULL){
                    if(MY_STRSTR(handle->exec->content, handle->exec_expect)){
                        handle->exec_finish = 1;
                        rtos_sem_post(handle->exec_sem);
                        rtos_sem_wait(handle->sem, 500);
                    }
                }
            }
            continue;
        }
    }
}

/**
  * @brief AT框架初始化.
  * @para1 SAT_Obj对象,需要是一个全局变量,唯一标识一个AT框架操作结构.定义时就必须清0,即={0}.
  * @para2 
  * @para3 
  * @para4 
  * @para5 
  * @expl  可多次调用,不用担心会重复初始化.已初始化过的内容不会变.
  * @return MY_OK:成功 MY_ERR:失败.
  */
int AT_init(SAT_Obj *handle, SAT_Uart *uart, const char *module_name, SAT_Event *event_table, uInt event_num)
{
    handle->uart_no = uart->uart_no;
    
    AT_uart_init(uart->uart_no, uart->baud, uart->databit, uart->stopbit, uart->parity);
    
    if(handle->have_inited == 0){
        handle->r_size = AT_OBJ_BUF_SIZE;
        handle->r_index = 0;
        MY_MEMSET(handle->r_buf, 0, handle->r_size);
        
        rtos_mutex_init(&handle->mutex);
        rtos_sem_init(&handle->sem, 0);
        rtos_sem_init(&handle->exec_sem, 0);
        AT_set_tail(handle, "\r\n");
        
        handle->event = event_table;
        handle->event_num = event_num;

        //对优先级无特殊要求,当然,越高用起来越丝滑
        rtos_create_task(module_name, AT_parse_task, (uInt)handle, 1024, 24/*TASK_PRI_GENERAL*/);
        
        handle->have_inited = 1;
    }
    
    return MY_OK;
}

